home *** CD-ROM | disk | FTP | other *** search
/ Software of the Month Club 2000 October / Software of the Month - Ultimate Collection Shareware 277.iso / pc / PROGRAMS / UTILITY / WINLINUX / DATA1.CAB / programs_-_kernel_source / SCRIPTS / TKPARSE.C < prev    next >
C/C++ Source or Header  |  1999-09-17  |  13KB  |  610 lines

  1. /*
  2.  * tkparse.c
  3.  *
  4.  * Eric Youngdale was the original author of xconfig.
  5.  * Michael Elizabeth Chastain (mec@shout.net) is the current maintainer.
  6.  *
  7.  * Parse a config.in file and translate it to a wish script.
  8.  * This task has three parts:
  9.  *
  10.  *   tkparse.c    tokenize the input
  11.  *   tkcond.c   transform 'if ...' statements
  12.  *   tkgen.c    generate output
  13.  *
  14.  * Change History
  15.  *
  16.  * 7 January 1999, Michael Elizabeth Chastain, <mec@shout.net>
  17.  * - Teach dep_tristate about a few literals, such as:
  18.  *     dep_tristate 'foo' CONFIG_FOO m
  19.  *   Also have it print an error message and exit on some parse failures.
  20.  *
  21.  * 14 January 1999, Michael Elizabeth Chastain, <mec@shout.net>
  22.  * - Don't fclose stdin.  Thanks to Tony Hoyle for nailing this one.
  23.  *
  24.  * 14 January 1999, Michael Elizabeth Chastain, <mec@shout.net>
  25.  * - Steam-clean this file.  I tested this by generating kconfig.tk for
  26.  *   every architecture and comparing it character-for-character against
  27.  *   the output of the old tkparse.
  28.  *
  29.  * 23 January 1999, Michael Elizabeth Chastain, <mec@shout.net>
  30.  * - Remove bug-compatible code.
  31.  *
  32.  * TO DO:
  33.  * - xconfig is at the end of its life cycle.  Contact <mec@shout.net> if
  34.  *   you are interested in working on the replacement.
  35.  */
  36.  
  37. #include <stdio.h>
  38. #include <stdlib.h>
  39. #include <string.h>
  40.  
  41. #include "tkparse.h"
  42.  
  43. static struct kconfig * config_list = NULL;
  44. static struct kconfig * config_last = NULL;
  45. static const char * current_file = "<unknown file>";
  46. static int lineno = 0;
  47.  
  48. static void do_source( const char * );
  49.  
  50. #undef strcmp
  51. int my_strcmp( const char * s1, const char * s2 ) { return strcmp( s1, s2 ); }
  52. #define strcmp my_strcmp
  53.  
  54.  
  55.  
  56. /*
  57.  * Report a syntax error.
  58.  */
  59. static void syntax_error( const char * msg )
  60. {
  61.     fprintf( stderr, "%s: %d: %s\n", current_file, lineno, msg );
  62.     exit( 1 );
  63. }
  64.  
  65.  
  66.  
  67. /*
  68.  * Get a string.
  69.  */
  70. static const char * get_string( const char * pnt, char ** label )
  71. {
  72.     const char * word;
  73.  
  74.     word = pnt;
  75.     for ( ; ; )
  76.     {
  77.     if ( *pnt == '\0' || *pnt == ' ' || *pnt == '\t' )
  78.         break;
  79.     pnt++;
  80.     }
  81.  
  82.     *label = malloc( pnt - word + 1 );
  83.     memcpy( *label, word, pnt - word );
  84.     (*label)[pnt - word] = '\0';
  85.  
  86.     if ( *pnt != '\0' )
  87.     pnt++;
  88.     return pnt;
  89. }
  90.  
  91.  
  92.  
  93. /*
  94.  * Get a quoted string.
  95.  * Insert a '\' before any characters that need quoting.
  96.  */
  97. static const char * get_qstring( const char * pnt, char ** label )
  98. {
  99.     char quote_char;
  100.     char newlabel [1024];
  101.     char * pnt1;
  102.  
  103.     /* advance to the open quote */
  104.     for ( ; ; )
  105.     {
  106.     if ( *pnt == '\0' )
  107.         return pnt;
  108.     quote_char = *pnt++;
  109.     if ( quote_char == '"' || quote_char == '\'' )
  110.         break;
  111.     }
  112.  
  113.     /* copy into an intermediate buffer */
  114.     pnt1 = newlabel;
  115.     for ( ; ; )
  116.     {
  117.     if ( *pnt == '\0' )
  118.         syntax_error( "unterminated quoted string" );
  119.     if ( *pnt == quote_char && pnt[-1] != '\\' )
  120.         break;
  121.  
  122.     /* copy the character, quoting if needed */
  123.     if ( *pnt == '"' || *pnt == '\'' || *pnt == '[' || *pnt == ']' )
  124.         *pnt1++ = '\\';
  125.     *pnt1++ = *pnt++;
  126.     }
  127.  
  128.     /* copy the label into a permanent location */
  129.     *pnt1++ = '\0';
  130.     *label = (char *) malloc( pnt1 - newlabel );
  131.     memcpy( *label, newlabel, pnt1 - newlabel );
  132.  
  133.     /* skip over last quote and next whitespace */
  134.     pnt++;
  135.     while ( *pnt == ' ' || *pnt == '\t' )
  136.     pnt++;
  137.     return pnt;
  138. }
  139.  
  140.  
  141. /*
  142.  * Tokenize an 'if' statement condition.
  143.  */
  144. static struct condition * tokenize_if( const char * pnt )
  145. {
  146.     struct condition * list;
  147.     struct condition * last;
  148.  
  149.     /* eat the open bracket */
  150.     while ( *pnt == ' ' || *pnt == '\t' )
  151.     pnt++;
  152.     if ( *pnt != '[' )
  153.     syntax_error( "bad 'if' condition" );
  154.     pnt++;
  155.  
  156.     list = last = NULL;
  157.     for ( ; ; )
  158.     {
  159.     struct condition * cond;
  160.  
  161.     /* advance to the next token */
  162.     while ( *pnt == ' ' || *pnt == '\t' )
  163.         pnt++;
  164.     if ( *pnt == '\0' )
  165.         syntax_error( "unterminated 'if' condition" );
  166.     if ( *pnt == ']' )
  167.         return list;
  168.  
  169.     /* allocate a new token */
  170.     cond = malloc( sizeof(*cond) );
  171.     memset( cond, 0, sizeof(*cond) );
  172.     if ( last == NULL )
  173.         { list = last = cond; }
  174.     else
  175.         { last->next = cond; last = cond; }
  176.  
  177.     /* determine the token value */
  178.     if ( *pnt == '-' && pnt[1] == 'a' )
  179.         { cond->op = op_and;  pnt += 2; continue; }
  180.  
  181.     if ( *pnt == '-' && pnt[1] == 'o' )
  182.         { cond->op = op_or;   pnt += 2; continue; }
  183.  
  184.     if ( *pnt == '!' && pnt[1] == '=' )
  185.         { cond->op = op_neq;  pnt += 2; continue; }
  186.  
  187.     if ( *pnt == '=' )
  188.         { cond->op = op_eq;   pnt += 1; continue; }
  189.  
  190.     if ( *pnt == '!' )
  191.         { cond->op = op_bang; pnt += 1; continue; }
  192.  
  193.     if ( *pnt == '"' )
  194.     {
  195.         const char * word;
  196.  
  197.         /* advance to the word */
  198.         pnt++;
  199.         if ( *pnt == '$' )
  200.         { cond->op = op_variable; pnt++; }
  201.         else
  202.         { cond->op = op_constant; }
  203.  
  204.         /* find the end of the word */
  205.         word = pnt;
  206.         for ( ; ; )
  207.         {
  208.         if ( *pnt == '\0' )
  209.             syntax_error( "unterminated double quote" );
  210.         if ( *pnt == '"' )
  211.             break;
  212.         pnt++;
  213.         }
  214.  
  215.         /* store a copy of this word */
  216.         {
  217.         char * str = malloc( pnt - word + 1 );
  218.         memcpy( str, word, pnt - word );
  219.         str [pnt - word] = '\0';
  220.         cond->str = str;
  221.         }
  222.  
  223.         pnt++;
  224.         continue;
  225.     }
  226.  
  227.     /* unknown token */
  228.     syntax_error( "bad if condition" );
  229.     }
  230. }
  231.  
  232.  
  233.  
  234. /*
  235.  * Tokenize a choice list.  Choices appear as pairs of strings;
  236.  * note that I am parsing *inside* the double quotes.  Ugh.
  237.  */
  238. static const char * tokenize_choices( struct kconfig * cfg_choose,
  239.     const char * pnt )
  240. {
  241.     for ( ; ; )
  242.     {
  243.     struct kconfig * cfg;
  244.  
  245.     /* skip whitespace */
  246.     while ( *pnt == ' ' || *pnt == '\t' )
  247.         pnt++;
  248.     if ( *pnt == '\0' )
  249.         return pnt;
  250.  
  251.     /* allocate a new kconfig line */
  252.     cfg = malloc( sizeof(*cfg) );
  253.     memset( cfg, 0, sizeof(*cfg) );
  254.     if ( config_last == NULL )
  255.         { config_last = config_list = cfg; }
  256.     else
  257.         { config_last->next = cfg; config_last = cfg; }
  258.  
  259.     /* fill out the line */
  260.     cfg->token      = token_choice_item;
  261.     cfg->cfg_parent = cfg_choose;
  262.     pnt = get_string( pnt, &cfg->label );
  263.     while ( *pnt == ' ' || *pnt == '\t' )
  264.         pnt++;
  265.     pnt = get_string( pnt, &cfg->optionname );
  266.     }
  267.  
  268.     return pnt;
  269. }
  270.  
  271.  
  272.  
  273.  
  274.  
  275. /*
  276.  * Tokenize one line.
  277.  */
  278. static void tokenize_line( const char * pnt )
  279. {
  280.     static struct kconfig * last_menuoption = NULL;
  281.     enum e_token token;
  282.     struct kconfig * cfg;
  283.  
  284.     /* skip white space */
  285.     while ( *pnt == ' ' || *pnt == '\t' )
  286.     pnt++;
  287.  
  288.     /*
  289.      * categorize the next token
  290.      */
  291.  
  292. #define match_token(t, s) \
  293.     if (strncmp(pnt, s, strlen(s)) == 0) { token = t; pnt += strlen(s); break; }
  294.  
  295.     token = token_UNKNOWN;
  296.     switch ( *pnt )
  297.     {
  298.     default:
  299.     break;
  300.  
  301.     case '#':
  302.     case '\0':
  303.     return;
  304.  
  305.     case 'b':
  306.     match_token( token_bool, "bool" );
  307.     break;
  308.  
  309.     case 'c':
  310.     match_token( token_choice_header, "choice"  );
  311.     match_token( token_comment, "comment" );
  312.     break;
  313.  
  314.     case 'd':
  315.     match_token( token_define_bool, "define_bool" );
  316.     match_token( token_dep_tristate, "dep_tristate" );
  317.     break;
  318.  
  319.     case 'e':
  320.     match_token( token_else, "else" );
  321.     match_token( token_endmenu, "endmenu" );
  322.     break;
  323.  
  324.     case 'f':
  325.     match_token( token_fi, "fi" );
  326.     break;
  327.  
  328.     case 'h':
  329.     match_token( token_hex, "hex" );
  330.     break;
  331.  
  332.     case 'i':
  333.     match_token( token_if, "if" );
  334.     match_token( token_int, "int" );
  335.     break;
  336.  
  337.     case 'm':
  338.     match_token( token_mainmenu_name, "mainmenu_name" );
  339.     match_token( token_mainmenu_option, "mainmenu_option" );
  340.     break;
  341.  
  342.     case 's':
  343.     match_token( token_source, "source" );
  344.     match_token( token_string, "string" );
  345.     break;
  346.  
  347.     case 't':
  348.     match_token( token_then, "then" );
  349.     match_token( token_tristate, "tristate" );
  350.     break;
  351.  
  352.     case 'u':
  353.     match_token( token_unset, "unset" );
  354.     break;
  355.     }
  356.  
  357. #undef match_token
  358.  
  359.     if ( token == token_source )
  360.     {
  361.     while ( *pnt == ' ' || *pnt == '\t' )
  362.         pnt++;
  363.     do_source( pnt );
  364.     return;
  365.     }
  366.  
  367.     if ( token == token_then )
  368.     {
  369.     if ( config_last != NULL && config_last->token == token_if )
  370.         return;
  371.     syntax_error( "bogus 'then'" );
  372.     }
  373.  
  374.     if ( token == token_unset )
  375.     {
  376.     fprintf( stderr, "Ignoring 'unset' command\n" );
  377.     return;
  378.     }
  379.  
  380.     if ( token == token_UNKNOWN )
  381.     syntax_error( "unknown command" );
  382.  
  383.     /*
  384.      * Allocate an item.
  385.      */
  386.     cfg = malloc( sizeof(*cfg) );
  387.     memset( cfg, 0, sizeof(*cfg) );
  388.     if ( config_last == NULL )
  389.     { config_last = config_list = cfg; }
  390.     else
  391.     { config_last->next = cfg; config_last = cfg; }
  392.  
  393.     /*
  394.      * Tokenize the arguments.
  395.      */
  396.     while ( *pnt == ' ' || *pnt == '\t' )
  397.     pnt++;
  398.  
  399.     cfg->token = token;
  400.     switch ( token )
  401.     {
  402.     default:
  403.     syntax_error( "unknown token" );
  404.  
  405.     case token_bool:
  406.     case token_tristate:
  407.     pnt = get_qstring ( pnt, &cfg->label      );
  408.     pnt = get_string  ( pnt, &cfg->optionname );
  409.     break;
  410.  
  411.     case token_choice_header:
  412.     {
  413.         static int choose_number = 0;
  414.         char * choice_list;
  415.  
  416.         pnt = get_qstring ( pnt, &cfg->label  );
  417.         pnt = get_qstring ( pnt, &choice_list );
  418.         pnt = get_string  ( pnt, &cfg->value  );
  419.  
  420.         cfg->optionname = malloc( 32 );
  421.         sprintf( cfg->optionname, "tmpvar_%d", choose_number++ );
  422.  
  423.         tokenize_choices( cfg, choice_list );
  424.         free( choice_list );
  425.     }
  426.     break;
  427.  
  428.     case token_comment:
  429.     pnt = get_qstring(pnt, &cfg->label);
  430.     if ( last_menuoption != NULL )
  431.     {
  432.         pnt = get_qstring(pnt, &cfg->label);
  433.         last_menuoption->label = cfg->label;
  434.         last_menuoption = NULL;
  435.     }
  436.     break;
  437.  
  438.     case token_define_bool:
  439.     pnt = get_string( pnt, &cfg->optionname );
  440.     while ( *pnt == ' ' || *pnt == '\t' )
  441.         pnt++;
  442.     if      ( *pnt == 'n' || *pnt == 'N' ) cfg->value = "0";
  443.     else if ( *pnt == 'y' || *pnt == 'Y' ) cfg->value = "1";
  444.     else if ( *pnt == 'm' || *pnt == 'M' ) cfg->value = "2";
  445.     else
  446.     {
  447.         syntax_error( "unknown define_bool value" );
  448.     }
  449.     break;
  450.  
  451.     case token_dep_tristate:
  452.     pnt = get_qstring ( pnt, &cfg->label      );
  453.     pnt = get_string  ( pnt, &cfg->optionname );
  454.  
  455.     while ( *pnt == ' ' || *pnt == '\t' )
  456.         pnt++;
  457.  
  458.     if ( ( pnt[0] == 'Y'  || pnt[0] == 'M' || pnt[0] == 'N'
  459.     ||     pnt[0] == 'y'  || pnt[0] == 'm' || pnt[0] == 'n'  )
  460.     &&   ( pnt[1] == '\0' || pnt[1] == ' ' || pnt[1] == '\t' ) )
  461.     {
  462.         /* dep_tristate 'foo' CONFIG_FOO m */
  463.         if      ( pnt[0] == 'Y' || pnt[0] == 'y' )
  464.         cfg->depend = strdup( "CONSTANT_Y" );
  465.         else if ( pnt[0] == 'M' || pnt[0] == 'm' )
  466.         cfg->depend = strdup( "CONSTANT_M" );
  467.         else
  468.         cfg->depend = strdup( "CONSTANT_N" );
  469.         pnt++;
  470.     }
  471.     else if ( *pnt == '$' )
  472.     {
  473.         pnt++;
  474.         pnt = get_string( pnt, &cfg->depend );
  475.     }
  476.     else
  477.     {
  478.         syntax_error( "can't handle dep_tristate condition" );
  479.     }
  480.  
  481.     /*
  482.      * Create a conditional for this object's dependency.
  483.      */
  484.     {
  485.         char fake_if [1024];
  486.         sprintf( fake_if, "[ \"$%s\" = \"y\" -o \"$%s\" = \"m\" ]; then",
  487.         cfg->depend, cfg->depend );
  488.         cfg->cond = tokenize_if( fake_if );
  489.     }
  490.     break;
  491.  
  492.     case token_else:
  493.     case token_endmenu:
  494.     case token_fi:
  495.     break;
  496.  
  497.     case token_hex:
  498.     case token_int:
  499.     case token_string:
  500.     pnt = get_qstring ( pnt, &cfg->label      );
  501.     pnt = get_string  ( pnt, &cfg->optionname );
  502.     pnt = get_string  ( pnt, &cfg->value      );
  503.     break;
  504.  
  505.     case token_if:
  506.     cfg->cond = tokenize_if( pnt );
  507.     break;
  508.  
  509.     case token_mainmenu_name:
  510.     pnt = get_qstring( pnt, &cfg->label );
  511.     break;
  512.  
  513.     case token_mainmenu_option:
  514.     if ( strncmp( pnt, "next_comment", 12 ) == 0 )
  515.         last_menuoption = cfg;
  516.     else
  517.         pnt = get_qstring( pnt, &cfg->label );
  518.     break;
  519.     }
  520.  
  521.     return;
  522. }
  523.  
  524.  
  525.  
  526. /*
  527.  * Implement the "source" command.
  528.  */
  529. static void do_source( const char * filename )
  530. {
  531.     char buffer [1024];
  532.     FILE * infile;
  533.     const char * old_file;
  534.     int old_lineno;
  535.     int offset;
  536.  
  537.     /* open the file */
  538.     if ( strcmp( filename, "-" ) == 0 )
  539.     infile = stdin;
  540.     else
  541.     infile = fopen( filename, "r" );
  542.  
  543.     /* if that failed, try ../filename */
  544.     if ( infile == NULL )
  545.     {
  546.     sprintf( buffer, "../%s", filename );
  547.     infile = fopen( buffer, "r" );
  548.     }
  549.  
  550.     if ( infile == NULL )
  551.     {
  552.     sprintf( buffer, "unable to open %s", filename );
  553.     syntax_error( buffer );
  554.     }
  555.  
  556.     /* push the new file name and line number */
  557.     old_file     = current_file;
  558.     old_lineno   = lineno;
  559.     current_file = filename;
  560.     lineno       = 0;
  561.  
  562.     /* read and process lines */
  563.     for ( offset = 0; ; )
  564.     {
  565.     char * pnt;
  566.  
  567.     /* read a line */
  568.     fgets( buffer + offset, sizeof(buffer) - offset, infile );
  569.     if ( feof( infile ) )
  570.         break;
  571.     lineno++;
  572.  
  573.     /* strip the trailing return character */
  574.     pnt = buffer + strlen(buffer) - 1;
  575.     if ( *pnt == '\n' )
  576.         *pnt-- = '\0';
  577.  
  578.     /* eat \ NL pairs */
  579.     if ( *pnt == '\\' )
  580.     {
  581.         offset = pnt - buffer;
  582.         continue;
  583.     }
  584.  
  585.     /* tokenize this line */
  586.     tokenize_line( buffer );
  587.     offset = 0;
  588.     }
  589.  
  590.     /* that's all, folks */
  591.     if ( infile != stdin )
  592.     fclose( infile );
  593.     current_file = old_file;
  594.     lineno       = old_lineno;
  595.     return;
  596. }
  597.  
  598.  
  599.  
  600. /*
  601.  * Main program.
  602.  */
  603. int main( int argc, const char * argv [] )
  604. {
  605.     do_source        ( "-"         );
  606.     fix_conditionals ( config_list );
  607.     dump_tk_script   ( config_list );
  608.     return 0;
  609. }
  610.